use std::borrow::Cow;

use serial_port::{SerialPort, SharedPort};
use tauri::Window;
use tokio::sync::Mutex;
use tokio_serial::available_ports;

use crate::error::{AppErr, ErrConv};

mod serial_port;
mod frame;

pub const RECV_EVENT: &'static str = "recv_event";

static PORT: Mutex<Option<SharedPort>> = Mutex::const_new(None);

#[tauri::command]
pub fn active_ports() -> Result<Vec<String>, AppErr> {

    let ports = available_ports().conv()?;
    let mut vec = Vec::with_capacity(ports.len());

    for item in ports {
        vec.push(item.port_name);
    }

    Ok(vec)
}

#[tauri::command]
pub async fn is_open() -> bool {
    PORT.lock().await.is_some()
}

#[tauri::command]
pub async fn open_port(window: Window, name: String) -> Result<(), AppErr> {
    
    let mut p = PORT.lock().await;
    match p.as_ref() {
        Some(p) => { p.close() }
        None => {}
    };

    let port = SerialPort::new(window, &name)?;
    *p = Some(port);

    Ok(())
}

#[tauri::command]
pub async fn write_port(buf: Box<[u8]>) -> Result<(), AppErr> {

    let p = PORT.lock().await;

    match p.as_ref() {
        Some(p) => p.write(&buf).await?,
        None => return Err(AppErr::Custom(Cow::Borrowed("串口没有打开 或者 已经关闭")))
    };

    Ok(())
}

#[tauri::command]
pub async fn close_port() -> Result<(), AppErr> {

    let mut p = PORT.lock().await;

    match p.as_ref() {
        Some(p) => p.close(),
        None => {}
    };

    *p = None;

    Ok(())
}